package imp

import (
	sqlc "bgs/db/sqlc"
	afterSaleGrpc "bgs/grpc/gen/service/after_sale"
	"bgs/grpc/gen/shared/message"
	"bgs/grpc/imp/enums"
	afterSaleService "bgs/service/afterSale"
	"bgs/service/model"
	orderService "bgs/service/order"
	"bgs/util"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"
)

/****************************************************************************************/
/*                               after_sale entity of helper                            */
/****************************************************************************************/

// 这里还是使用*model.OrderExtInfo, 因为售后(after_sale)所需的信息model.OrderExtInfo里都有
func transformAfterSaleEntityToPb(orderExtInfoPtr *model.OrderExtInfo, afterSaleDetails []sqlc.AfterSaleDetail) func(o sqlc.AfterSale) *afterSaleGrpc.AfterSaleEntity {

	mapOfOrderDetails := util.SliceToMapString(util.ToGenericSlice(orderExtInfoPtr.Details), func(m map[string]interface{}, v interface{}) {
		val := v.(*model.OrderDetailInExtInfo)
		m[val.ProductSpecKey] = v
	})

	return func(o sqlc.AfterSale) *afterSaleGrpc.AfterSaleEntity {
		return &afterSaleGrpc.AfterSaleEntity{
			Id:                       o.ID,
			Applied:                  sqlc.GoTimeToPBTimestamp(o.Applied),
			SerialNumber:             o.SerialNumber,
			BrandId:                  o.BrandID,
			StoreId:                  o.StoreID.Int64,
			DistributorId:            o.DistributorID.Int64,
			DistributorUserId:        o.DistributorUserID.Int64,
			ActivityId:               o.ActivityID,
			OrderId:                  o.OrderID,
			OrderSerialNumber:        o.OrderSerialNumber,
			UserId:                   o.UserID,
			SpokesmanId:              o.SpokesmanID.Int64,
			SpokesmanUserId:          o.SpokesmanUserID.Int64,
			Type:                     o.Type,
			Audited:                  sqlc.GoNTimeToPBTimestamp(o.Audited),
			Cancelled:                sqlc.GoNTimeToPBTimestamp(o.Cancelled),
			Sended:                   sqlc.GoNTimeToPBTimestamp(o.Sended),
			Received:                 sqlc.GoNTimeToPBTimestamp(o.Received),
			Refunded:                 sqlc.GoNTimeToPBTimestamp(o.Refunded),
			AfterSaleStatus:          o.AfterSaleStatus,
			Amount:                   o.Amount,
			IsWholeRefund:            o.IsWholeRefund,
			Remark:                   o.Remark.String,
			AfterSaleReason:          o.AfterSaleReason,
			CloseReason:              o.CloseReason.String,
			RefuseAfterSaleReason:    o.RefuseAfterSaleReason.String,
			ReturnGoodsAddressInfo:   string(sqlc.NRawMessageToRawMessage(o.ReturnGoodsAddressInfo)),
			ReturnGoodsLogisticsInfo: string(sqlc.NRawMessageToRawMessage(o.ReturnGoodsLogisticsInfo)),
			AfterSaleDetails:         mapAfterSaleDetailEntityToPb(afterSaleDetails, mapOfOrderDetails, transformAfterSaleDetailEntityToPb),
			BrandName:                orderExtInfoPtr.BrandName,
			BrandPic:                 orderExtInfoPtr.BrandPic,
			BrandTel:                 orderExtInfoPtr.BrandTel,
			StoreName:                orderExtInfoPtr.StoreName,
			StorePic:                 orderExtInfoPtr.StorePic,
			StoreTel:                 orderExtInfoPtr.StoreTel,
			ActivityOwner:            orderExtInfoPtr.ActivityOwner,
			ActivityName:             orderExtInfoPtr.ActivityName,
			ActivityTel:              orderExtInfoPtr.ActivityTel,
			ParentActivityOwner:      orderExtInfoPtr.ParentActivityOwner,
			ParentActivityName:       orderExtInfoPtr.ParentActivityName,
			ParentActivityTel:        orderExtInfoPtr.ParentActivityTel,
			BuyerNickName:            orderExtInfoPtr.BuyerNickName,
			BuyerPic:                 orderExtInfoPtr.BuyerPic,
			BuyerTel:                 orderExtInfoPtr.BuyerTel,
		}
	}
}

func transformAfterSaleDetailEntityToPb(
	ext *model.OrderDetailInExtInfo,
) func(*sqlc.AfterSaleDetail) *afterSaleGrpc.AfterSaleDetailItemEntity {
	return func(oPtr *sqlc.AfterSaleDetail) *afterSaleGrpc.AfterSaleDetailItemEntity {
		return &afterSaleGrpc.AfterSaleDetailItemEntity{
			Id:                   ext.OrderDetailID,
			ProductId:            ext.ProductID,
			ProductName:          ext.ProductName,
			ProductSpecId:        ext.ProductSpecID,
			ProductSpecKey:       ext.ProductSpecKey,
			ProductSpecName:      ext.ProductSpecName,
			ProductWithSpecName:  ext.ProductWithSpecName,
			StorageKey:           ext.StorageKey,
			ProductPics:          ext.ProductPics,
			Quantity:             oPtr.Quantity,
			Price:                ext.Price,
			GoodsReceivingStatus: oPtr.GoodsReceivingStatus,
		}
	}
}

func mapAfterSaleDetailEntityToPb(list []sqlc.AfterSaleDetail, mapOfOrderDetails map[string]interface{}, f func(*model.OrderDetailInExtInfo) func(*sqlc.AfterSaleDetail) *afterSaleGrpc.AfterSaleDetailItemEntity) []*afterSaleGrpc.AfterSaleDetailItemEntity {
	r := make([]*afterSaleGrpc.AfterSaleDetailItemEntity, len(list))
	for i, item := range list {
		val := mapOfOrderDetails[item.ProductSpecKey].(*model.OrderDetailInExtInfo)
		r[i] = f(val)(&item)
	}
	return r
}

/****************************************************************************************/
/*                               after_sale entity of cmd                               */
/****************************************************************************************/

// ApplyAfterSale 用户申请售后
func (s *AfterSaleAPIService) ApplyAfterSale(ctx context.Context, req *afterSaleGrpc.ApplyAfterSaleRequest) (res *afterSaleGrpc.ApplyAfterSaleResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->ApplyAfterSale:%v", req)
	res = &afterSaleGrpc.ApplyAfterSaleResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail

	var createAfterSaleDetails []*afterSaleService.CreateAfterSaleDetailItem
	for _, item := range req.ApplyAfterSaleDetails {
		createAfterSaleDetails = append(createAfterSaleDetails, &afterSaleService.CreateAfterSaleDetailItem{
			OrderDetailID:        item.OrderDetailId,
			Quantity:             item.Quantity,
			GoodsReceivingStatus: enums.EnumGoodsReceivingStatus(item.GoodsReceivingStatus),
		})
	}

	orderSerialNumber := req.OrderSerialNumber

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		order, err = q.GetOrderBySerialNumber(ctx, orderSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("a 获取订单 ok")

		if order.IsInAfterSale {
			err = fmt.Errorf("当前订单已处于一次售后中,请先处理")
			return
		}

		// 申请售后时间 < 订单的售后截止时间 ,并且订单本身不在售后中
		if order.AfterSaleDeadlineAt.Valid {
			if order.AfterSaleDeadlineAt.Time.Before(time.Now()) {
				err = fmt.Errorf("当前订单已截止申请售后")
				return
			}
		}

		afterSale, afterSaleDetails, err = s.afterSaleService.CreateAfterSale(ctx, q, afterSaleService.CreateAfterSaleParam{
			OrderPtr:        &order,
			UserID:          req.UserId,
			Type:            enums.EnumAfterSaleType(req.Type),
			Remark:          req.Remark,
			AfterSaleReason: req.AfterSaleReason,
			Details:         createAfterSaleDetails,
		})
		if err != nil {
			return
		}
		slog.Infof("a 创建售后单+售后明细 ok")

		order, err = s.orderService.MarkOrderIntoApplyAfterSale(ctx, q, orderService.MarkOrderParam{
			OrderPtr: &order,
		})
		if err != nil {
			return
		}
		slog.Infof("b 标记订单申请售后 ok")

		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

// CancelAfterSale 用户撤销售后
func (s *AfterSaleAPIService) CancelAfterSale(ctx context.Context, req *afterSaleGrpc.CancelAfterSaleRequest) (res *afterSaleGrpc.CancelAfterSaleResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->CancelAfterSale:%v", req)
	res = &afterSaleGrpc.CancelAfterSaleResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail

	afterSaleSerialNumber := req.AfterSaleSerialNumber
	userID := req.UserId

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("1 获取售后单 ok")

		if afterSale.AfterSaleStatus != string(enums.AfterSaleStatusCreated) {
			err = fmt.Errorf("售后单状态不允许:%s", afterSale.AfterSaleStatus)
			return
		}

		if afterSale.UserID != userID {
			err = fmt.Errorf("售后单不属于请求的用户:%d", userID)
			return
		}

		afterSale, err = s.afterSaleService.CancelAfterSale(ctx, q, afterSale.ID)
		if err != nil {
			return
		}
		slog.Infof("2 取消售后单 ok")

		order, err = s.orderService.DoAfterSaleRefuseOrCancel(ctx, q, orderService.DoAfterSaleRefuseOrCancelParam{
			AfterSalePtr: &afterSale,
		})
		if err != nil {
			return
		}
		slog.Infof("3 售后单被用户取消后订单处理 ok")

		afterSaleDetails, err = q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, afterSale.ID)
		if err != nil {
			return
		}
		slog.Infof("3 获取售后单明细 ok")

		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

// AuditAfterSale 商家审核售后
func (s *AfterSaleAPIService) AuditAfterSale(ctx context.Context, req *afterSaleGrpc.AuditAfterSaleRequest) (res *afterSaleGrpc.AuditAfterSaleResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->AuditAfterSale:%v", req)
	res = &afterSaleGrpc.AuditAfterSaleResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail
	var needSendNotify bool

	afterSaleSerialNumber := req.AfterSaleSerialNumber
	brandID := req.BrandId
	isRefuse := req.IsRefuse

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("1 获取售后单 ok")

		if afterSale.BrandID != brandID {
			err = fmt.Errorf("售后单不属于请求的品牌:%d", brandID)
			return
		}

		order, err = q.GetOrder(ctx, afterSale.OrderID)
		if err != nil {
			return
		}
		slog.Infof("2 获取订单 ok")

		afterSale, needSendNotify, err = s.afterSaleService.AuditAfterSale(ctx, q, afterSaleService.AuditAfterSaleParam{
			AfterSalePtr: &afterSale,
			OrderPtr:     &order,
			IsRefuse:     isRefuse,
			RefuseReason: req.RefuseReason,
		})
		if err != nil {
			return
		}

		if isRefuse {
			slog.Infof("3 审核售后单(拒绝) ok")
			order, err = s.orderService.DoAfterSaleRefuseOrCancel(ctx, q, orderService.DoAfterSaleRefuseOrCancelParam{
				AfterSalePtr: &afterSale,
			})
			if err != nil {
				return
			}
			slog.Infof("4 售后单被拒绝后订单处理 ok")

		} else {
			slog.Infof("3 审核售后单(同意) ok")
			order, err = s.orderService.MarkOrderIntoAfterSale(ctx, q, orderService.MarkOrderParam{
				OrderPtr: &order,
			})
			if err != nil {
				return
			}
			slog.Infof("4 标记订单进入售后 ok")

			if afterSale.Type == string(enums.AfterSaleTypeRefund) {
				slog.Infof("当前售后为仅退款")
				orderPtr := &order
				afterSalePtr := &afterSale
				err = s.afterSaleService.RefundAfterSale(ctx, q, afterSaleService.RefundAfterSaleParam{
					OrderPtr:     orderPtr,
					AfterSalePtr: afterSalePtr,
				})
				if err != nil {
					return err
				}
				slog.Infof("5 计算本次退款金额并标记售后单为退款中 ok")

				slog.Infof("5x 原来立即提交第三方交易平台退款 ok")
				wxRefundID, err := s.paymentService.Refund(ctx, q, &order, &afterSale)
				if err != nil {
					return err
				}
				slog.Infof("6 提交微信退款 ok")

				err = s.afterSaleService.UpdateAfterSaleWxRefundID(ctx, q, afterSaleService.UpdateAfterSaleWxRefundIDParam{
					AfterSaleID: afterSale.ID,
					WxRefundID:  wxRefundID,
				})
				if err != nil {
					return err
				}
				slog.Infof("7 微信退款单号更新到售后单 ok")

				// var profitSharedAmount int64
				// profitSharedAmount, err = s.ledgerService.CalcProfitSharedAmount(ctx, q, &afterSale)
				// if err != nil {
				// 	return err
				// }
				// slog.Infof("4.1 已分账金额 profitSharedAmount=%d", profitSharedAmount)
				// if profitSharedAmount > 0 {
				// 	err = s.ledgerService.DoBeforeAfterSaleRefund(ctx, q, &order, &afterSale)
				// 	if err != nil {
				// 		return err
				// 	}
				// } else {

				// }
			} else {
				slog.Infof("5 当前售后为退货退款,等待用户寄出")
			}
		}
		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	if needSendNotify {
		notifyParam, genErr := s.afterSaleService.GenAfterSaleStatusChangedNotifyMessage(ctx, s.dao.Q, afterSaleSerialNumber)
		if genErr != nil {
			slog.Errorf("AuditAfterSale 发送售后状态变更消息失败:%s", genErr.Error())
			// 不影响主流程因此不赋值给err
		}
		sendErr := s.wxpMinaService.SendNotify(ctx, &notifyParam)
		if sendErr != nil {
			slog.Errorf("AuditAfterSale 发送mina售后状态变更提醒失败:%s", sendErr.Error())
		}
	}

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

// SendAfterSaleReturnGoods 用户确认售后退货发出
func (s *AfterSaleAPIService) SendAfterSaleReturnGoods(ctx context.Context, req *afterSaleGrpc.SendAfterSaleReturnGoodsRequest) (res *afterSaleGrpc.SendAfterSaleReturnGoodsResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->SendAfterSaleReturnGoods:%v", req)
	res = &afterSaleGrpc.SendAfterSaleReturnGoodsResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail

	afterSaleSerialNumber := req.AfterSaleSerialNumber
	userID := req.UserId

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("1 获取售后单 ok")

		if afterSale.AfterSaleStatus != string(enums.AfterSaleStatusWaitSend) {
			err = fmt.Errorf("售后单状态不允许:%s", afterSale.AfterSaleStatus)
			return
		}

		if afterSale.UserID != userID {
			err = fmt.Errorf("售后单不属于请求的用户:%d", userID)
			return
		}

		afterSale, err = s.afterSaleService.SendAfterSaleReturnGoods(ctx, q, afterSaleService.SendAfterSaleReturnGoodsParam{
			AfterSalePtr: &afterSale,
			PlatformID:   req.PlatformId,
			PlatformName: req.PlatformName,
			PlatformNo:   req.PlatformNo,
		})
		if err != nil {
			return
		}
		slog.Infof("2 用户确认售后退货发出 ok")

		afterSaleDetails, err = s.dao.Q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, afterSale.ID)
		if err != nil {
			return
		}
		slog.Infof("3 获取售后单明细 ok")

		order, err = q.GetOrder(ctx, afterSale.OrderID)
		if err != nil {
			return
		}
		slog.Infof("4 获取订单 ok")

		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

// ReceiveAfterSaleReturnGoods 商家同意或拒绝售后退货入库
func (s *AfterSaleAPIService) ReceiveAfterSaleReturnGoods(ctx context.Context, req *afterSaleGrpc.ReceiveAfterSaleReturnGoodsRequest) (res *afterSaleGrpc.ReceiveAfterSaleReturnGoodsResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->ReceiveAfterSaleReturnGoods:%v", req)
	res = &afterSaleGrpc.ReceiveAfterSaleReturnGoodsResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail
	var needSendNotify bool

	afterSaleSerialNumber := req.AfterSaleSerialNumber
	brandID := req.BrandId

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("1 获取售后单 ok")

		if afterSale.AfterSaleStatus != string(enums.AfterSaleStatusSended) {
			err = fmt.Errorf("售后单状态不允许:%s", afterSale.AfterSaleStatus)
			return
		}

		if afterSale.BrandID != brandID {
			err = fmt.Errorf("售后单不属于请求的品牌:%d", brandID)
			return
		}

		order, err = q.GetOrder(ctx, afterSale.OrderID)
		if err != nil {
			return
		}
		slog.Infof("2 获取订单 ok")

		if req.AgreeReceive {
			afterSale, err = s.afterSaleService.ReceiveAfterSaleReturnGoods(ctx, q, afterSale.ID)
			if err != nil {
				return
			}
			slog.Infof("3 商家同意入库退货物品 ok")
			err = s.afterSaleService.RefundAfterSale(ctx, q, afterSaleService.RefundAfterSaleParam{
				OrderPtr:     &order,
				AfterSalePtr: &afterSale,
			})
			if err != nil {
				return err
			}
			slog.Infof("4 计算本次退款金额并标记售后单为退款中 ok")
			
			slog.Infof("5 立即提交第三方交易平台退款 ok")
			wxRefundID, err := s.paymentService.Refund(ctx, q, &order, &afterSale)
			if err != nil {
				return err
			}
			slog.Infof("6 提交微信退款 ok")

			err = s.afterSaleService.UpdateAfterSaleWxRefundID(ctx, q, afterSaleService.UpdateAfterSaleWxRefundIDParam{
				AfterSaleID: afterSale.ID,
				WxRefundID:  wxRefundID,
			})
			if err != nil {
				return err
			}
			slog.Infof("7 微信退款单号更新到售后单 ok")
			// var profitSharedAmount int64
			// profitSharedAmount, err = s.ledgerService.CalcProfitSharedAmount(ctx, q, &afterSale)
			// if err != nil {
			// 	return err
			// }
			// slog.Infof("4.1 已分账金额 profitSharedAmount=%d", profitSharedAmount)
			// if profitSharedAmount > 0 {
			// 	err = s.ledgerService.DoBeforeAfterSaleRefund(ctx, q, &order, &afterSale)
			// 	if err != nil {
			// 		return err
			// 	}
			// } else {
				
			// }
		} else {
			afterSale, err = s.afterSaleService.RefuseReceiveAfterSaleReturnGoods(ctx, q, afterSaleService.RefuseReceiveAfterSaleReturnGoodsParam{
				AfterSaleID:           afterSale.ID,
				RefuseAfterSaleReason: req.RefuseAfterSaleReason,
			})
			if err != nil {
				return
			}
			slog.Infof("3 商家拒绝入库退货物品 ok")

			order, err = s.orderService.DoAfterSaleRefuseOrCancel(ctx, q, orderService.DoAfterSaleRefuseOrCancelParam{
				AfterSalePtr: &afterSale,
			})
			if err != nil {
				return
			}
			slog.Infof("3 售商家拒绝入库退货物品后订单处理 ok")

			needSendNotify = true
		}

		afterSaleDetails, err = s.dao.Q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, afterSale.ID)
		if err != nil {
			return
		}
		slog.Infof("4 获取售后单明细 ok")
		return
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	if needSendNotify {
		notifyParam, genErr := s.afterSaleService.GenAfterSaleStatusChangedNotifyMessage(ctx, s.dao.Q, afterSaleSerialNumber)
		if genErr != nil {
			slog.Errorf("ReceiveAfterSaleReturnGoods 发送售后状态变更消息失败:%s", genErr.Error())
			// 不影响主流程因此不赋值给err
		}
		sendErr := s.wxpMinaService.SendNotify(ctx, &notifyParam)
		if sendErr != nil {
			slog.Errorf("ReceiveAfterSaleReturnGoods 发送mina售后状态变更提醒失败:%s", sendErr.Error())
		}
	}

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

/****************************************************************************************/
/*                               after_sale entity of query                             */
/****************************************************************************************/

// GetAfterSale 获取售后单
func (s *AfterSaleAPIService) GetAfterSale(ctx context.Context, req *afterSaleGrpc.GetAfterSaleRequest) (res *afterSaleGrpc.GetAfterSaleResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->GetAfterSale:%v", req)
	res = &afterSaleGrpc.GetAfterSaleResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	var order sqlc.Order
	var afterSale sqlc.AfterSale
	var afterSaleDetails []sqlc.AfterSaleDetail
	q := s.dao.Q

	afterSaleSerialNumber := req.GetSerialNumber()
	if afterSaleSerialNumber != "" {
		afterSale, err = q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
	} else {
		afterSale, err = q.GetAfterSale(ctx, req.GetAfterSaleId())
	}
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    util.Int32IfThenElse(sql.ErrNoRows == err, 404, 400),
			Msg:     err.Error(),
		}
		return
	}
	slog.Infof("1 获取售后单 ok")

	afterSaleDetails, err = q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, afterSale.ID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    util.Int32IfThenElse(sql.ErrNoRows == err, 404, 400),
			Msg:     err.Error(),
		}
		return
	}
	slog.Infof("2 获取售后单明细 ok")

	order, err = q.GetOrder(ctx, afterSale.OrderID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	slog.Infof("3 获取订单 ok")

	orderExtInfoPtr := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfoPtr)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("unmarshal ext_info失败:%s", err.Error()),
		}
		return
	}
	res.AfterSale = transformAfterSaleEntityToPb(orderExtInfoPtr, afterSaleDetails)(afterSale)

	return
}

/****************************************************************************************/
/*                            after_saleJoins entity of helper                          */
/****************************************************************************************/

type AfterSaleProductSpecKeyToQuantityMap map[string]int32

func genAfterSaleProductSpecKeyToQuantityMapKey(afterSaleID int64, productSpecKey string) string {
	return fmt.Sprintf("%d:%s", afterSaleID, productSpecKey)
}

func transformAfterSaleItemForCEntityToPb(afterSaleDetails []sqlc.AfterSaleDetail) func(sqlc.ListAfterSaleWithOrderExtsAsUserSearcherRow) *afterSaleGrpc.AfterSaleItemForCEntity {
	return func(o sqlc.ListAfterSaleWithOrderExtsAsUserSearcherRow) *afterSaleGrpc.AfterSaleItemForCEntity {
		orderExtInfo := &model.OrderExtInfo{}
		json.Unmarshal(o.OrderExtInfo, orderExtInfo)

		afterSaleDetailsMap := make(AfterSaleProductSpecKeyToQuantityMap)
		util.Each(util.ToGenericSlice(afterSaleDetails), func(v interface{}) {
			val := v.(sqlc.AfterSaleDetail)
			k := genAfterSaleProductSpecKeyToQuantityMapKey(val.AfterSaleID, val.ProductSpecKey)
			afterSaleDetailsMap[k] = val.Quantity
		})

		var afterSaleDetailInfo []*model.OrderDetailInExtInfo
		util.Each(util.ToGenericSlice(orderExtInfo.Details), func(v interface{}) {
			val := v.(*model.OrderDetailInExtInfo)
			k := genAfterSaleProductSpecKeyToQuantityMapKey(o.ID, val.ProductSpecKey)
			if afterSaleQuantity, ok := afterSaleDetailsMap[k]; ok {
				val.Quantity = afterSaleQuantity
				afterSaleDetailInfo = append(afterSaleDetailInfo, val)
			}
		})

		return &afterSaleGrpc.AfterSaleItemForCEntity{
			Id:                   o.ID,
			Applied:              sqlc.GoTimeToPBTimestamp(o.Applied),
			SerialNumber:         o.SerialNumber,
			BrandId:              o.BrandID,
			ActivityId:           o.ActivityID,
			AfterSaleStatus:      o.AfterSaleStatus,
			Amount:               o.Amount,
			Type:                 o.Type,
			CloseReason:          o.CloseReason.String,
			AfterSaleSlimDetails: mapAfterSaleSlimDetailEntityToPb(afterSaleDetailInfo, transformAfterSaleSlimDetailEntityToPb),
			BrandName:            orderExtInfo.BrandName,
			BrandPic:             orderExtInfo.BrandPic,
			BrandTel:             orderExtInfo.BrandTel,
			ActivityName:         orderExtInfo.ActivityName,
			ActivityTel:          orderExtInfo.ActivityTel,
		}
	}
}

func mapAfterSaleItemForCEntityToPb(list []sqlc.ListAfterSaleWithOrderExtsAsUserSearcherRow, f func(sqlc.ListAfterSaleWithOrderExtsAsUserSearcherRow) *afterSaleGrpc.AfterSaleItemForCEntity) []*afterSaleGrpc.AfterSaleItemForCEntity {
	r := make([]*afterSaleGrpc.AfterSaleItemForCEntity, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

func transformAfterSaleSlimDetailEntityToPb(
	o *model.OrderDetailInExtInfo,
) *afterSaleGrpc.AfterSaleSlimDetailItemEntity {
	return &afterSaleGrpc.AfterSaleSlimDetailItemEntity{
		ProductId:           o.ProductID,
		ProductName:         o.ProductName,
		ProductSpecId:       o.ProductSpecID,
		ProductSpecKey:      o.ProductSpecKey,
		ProductSpecName:     o.ProductSpecName,
		ProductWithSpecName: o.ProductWithSpecName,
		StorageKey:          o.StorageKey,
		ProductPics:         o.ProductPics,
		Quantity:            o.Quantity,
		Price:               o.Price,
	}
}

func mapAfterSaleSlimDetailEntityToPb(list []*model.OrderDetailInExtInfo, f func(*model.OrderDetailInExtInfo) *afterSaleGrpc.AfterSaleSlimDetailItemEntity) []*afterSaleGrpc.AfterSaleSlimDetailItemEntity {
	r := make([]*afterSaleGrpc.AfterSaleSlimDetailItemEntity, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

/****************************************************************************************/
/*                            afterSaleJoins entity of query                            */
/****************************************************************************************/

// ListAfterSaleItemsForC 分页获取多个售后单(视角C)
func (s *AfterSaleAPIService) ListAfterSaleItemsForC(ctx context.Context, req *afterSaleGrpc.ListAfterSaleItemsForCRequest) (res *afterSaleGrpc.ListAfterSaleItemsForCResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("after_saleImp->ListAfterSaleItemsForC:%v", req)

	res = &afterSaleGrpc.ListAfterSaleItemsForCResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	q := s.dao.Q
	ts := sqlc.PBTimestampToGoTime(req.Ts)
	userID := req.UserId

	total, err := q.CountAfterSaleWithOrderExtsAsUserSearcher(ctx, sqlc.CountAfterSaleWithOrderExtsAsUserSearcherParams{
		Created: ts,
		UserID:  userID,
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	if total == 0 {
		// 不用继续调用listfunc
		return
	}

	afterSaleItemsForC, err := q.ListAfterSaleWithOrderExtsAsUserSearcher(ctx, sqlc.ListAfterSaleWithOrderExtsAsUserSearcherParams{
		Limit:   req.Limit,
		Offset:  req.Offset,
		Created: ts,
		UserID:  userID,
	})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	afterSaleIDs := util.MapToInt64(util.ToGenericSlice(afterSaleItemsForC), func(v interface{}) int64 {
		val := v.(sqlc.ListAfterSaleWithOrderExtsAsUserSearcherRow)
		return val.ID
	})

	afterSaleDetails, err := q.ListNormalAfterSaleDetailsByAfterSaleIDs(ctx, afterSaleIDs)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	res.Total = total
	res.AfterSaleItemsForC = mapAfterSaleItemForCEntityToPb(afterSaleItemsForC, transformAfterSaleItemForCEntityToPb(afterSaleDetails))
	return
}
